%{
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "libblogc/list.h"
#include "node.h"
#include "symbol.h"
#include "semantic.h"
#define YYERROR_VERBOSE 1
list_t prs_obs_list;                         /* list of observed stmts */
list_t prs_qry_list;                         /* list of queried stmts */

extern int yylineno;
extern char * yyfilename;
extern char * yytext;

int yylex(void);
int yyerror (char *s);
%}

%union 
{
  double          dbl_val;
  int             int_val;
  char          * str_val;
  struct node   * node_val;
  void          * list_val;
  struct symbol * sym_val;
}

/* punctuation */
%token T_COMMA T_SEMICOLON T_COLON
%token T_OPEN_PAREN T_CLOSE_PAREN
%token T_OPEN_BOX T_CLOSE_BOX
%token T_OPEN_BRACE T_CLOSE_BRACE

/* operators */
%left T_AND T_OR
%left T_EQUAL T_NOT_EQUAL
%token T_TILDE T_SHARP

/* keywords */
%token <int_val> T_TYPE T_RANDOM T_GUARANTEED T_NON_RANDOM T_ORIGIN
%token <int_val> T_OBS T_QUERY T_NULL T_IF T_THEN T_ELSEIF T_ELSE

%token <int_val> T_NATURAL_NUM
%token <dbl_val> T_REAL_NUM
%token <str_val> T_IDENTIFIER T_UNKNOWN T_STRING
%type <int_val> nt_function_category

%type <list_val> nt_objname nt_objname_list

%type <list_val> nt_opt_typename_list nt_typename_list
%type <list_val> nt_id_list nt_opt_paren_id_list 
%type <node_val> nt_id_equal_id
%type <list_val> nt_opt_paren_id_equal_id_list nt_id_equal_id_list

%type <node_val> nt_opt_paren_expr_list nt_expr_list
%type <node_val> nt_opt_box_expr_list 

%type <sym_val>  nt_typename nt_opt_typename
%type <node_val> nt_dependency_stmt nt_dependency_stmt_no_brace nt_dependency
%type <node_val> nt_expr nt_dependency_elseif nt_expr_cpd_call
%type <node_val> nt_expr_fn_call nt_expr_set_imp nt_expr_rkvector
%type <node_val> nt_expr_set_exp nt_opt_expr_set_cond nt_expr_identifier

%start nt_statement_list

%%

nt_statement_list : nt_statement
                  | nt_statement_list nt_statement
;

nt_statement : nt_type_statement
             | nt_guaranteed_statement
             | nt_declare_function
             | nt_define_function
             | nt_number_statement
             | nt_obs_statement
             | nt_query_statement
;

nt_type_statement : T_TYPE T_IDENTIFIER T_SEMICOLON
{
  if (symbol_table_new_type($2))
  {
    /* if the symbol already exists then its an error */
    char errmsg[1000];
    /* format the error message */
    sprintf(errmsg, "symbol %s redefined", $2);
    /* free the memory for the symbol */
    free($2);
    /* print the error and stop parsing */
    yyerror(errmsg);
    YYERROR;
  }
}

nt_guaranteed_statement : T_GUARANTEED nt_typename nt_objname_list
{
  symbol * typesym;
  list_t iter;

  typesym = $2;

  for (iter = list_next($3); iter; iter = list_next(iter))
  {
    nodeleaf leaf;
    leaf.pchar = (char *) list_item(iter);
    
    semantic_parse_guarobj(typesym, nodeleaf_alloc(NODETYPE_IDENTIFIER, leaf, 
                                                   yyfilename, yylineno));    
  }
  list_free($3);
}

nt_objname_list : nt_objname T_SEMICOLON
{
  $$ = $1;
}
                | nt_objname T_COMMA nt_objname_list
{
  $$ = $1;
  list_join($$, $3);                         /* deallocate $3 and copy it */
}

;

nt_objname : T_IDENTIFIER
{
  $$ = list_alloc1($1);
}
           | T_IDENTIFIER T_OPEN_BOX T_NATURAL_NUM T_CLOSE_BOX
{
  int i;
  
  /* the count of objects to be created must be positive */
  if ($3 < 1)
  {
    char errmsg[1000];
    /* format the error message */
    sprintf(errmsg, "incorrect number of objects: %d", $3);
    /* free the memory for the object name */
    free($1);
    /* print the error and stop parsing */
    yyerror(errmsg);
    YYERROR;    
  }

  $$ = list_alloc();
  for (i=0; i<$3; i++)
  {
    char * name;
    name = (char *)malloc(strlen($1)+10);
    sprintf(name, "%s%d", $1, i+1);
    list_insert_end($$, name);
  }
  free($1);                                  /* free the identifier string */
}

nt_declare_function : nt_function_category nt_typename T_IDENTIFIER 
                      nt_opt_typename_list T_SEMICOLON
{
  semantic_parse_fndecl($1, $2, $3, $4);
}

nt_function_category : T_RANDOM     
{
  $$ = SYMBOL_FN_CAT_RANDOM;
}
                     | T_NON_RANDOM 
{
  $$ = SYMBOL_FN_CAT_NONRANDOM;
}
                     | T_ORIGIN
{
  $$ = SYMBOL_FN_CAT_ORIGIN;
}


nt_opt_typename_list : /* empty */
{
  $$ = list_alloc();
}
                         | T_OPEN_PAREN nt_typename_list T_CLOSE_PAREN
{
  $$ = $2;
}


nt_typename_list : nt_typename
{
  $$ = list_alloc1($1);
}
                 | nt_typename T_COMMA nt_typename_list
{
 $$ = $3;
 list_insert_front($$, $1);
}

;

nt_typename : T_IDENTIFIER
{
  symbol * sym;
  sym = symbol_table_find($1);
  if ((NULL == sym) || (SYMBOL_B_TYPE != sym->symtype))
  {
    char errmsg[100];
    sprintf(errmsg, "%s is not a valid type", $1); /* format error message */
    free($1);                                /* free memory for the name */
    yyerror(errmsg);                         /* throw an error message ..*/
    YYERROR;                                 /* ..and stop parsing */
  }
  free($1);                                  /* free memory for the name */
  $$ = sym;
}

nt_define_function : T_IDENTIFIER nt_opt_paren_id_list
                     nt_dependency_stmt T_SEMICOLON
{
  semantic_parse_fndef($1, $2, $3);  
}


nt_opt_paren_id_list : /* empty */
{
  $$ = list_alloc();
}
                     | T_OPEN_PAREN nt_id_list T_CLOSE_PAREN
{
  $$ = $2;
}

nt_id_list : /* empty */
{
  $$ = list_alloc();
}
           | nt_expr_identifier
{
  $$ = list_alloc1($1);
}
           | nt_expr_identifier T_COMMA nt_id_list
{
  $$ = $3;
  list_insert_front($$, $1);
}

nt_dependency_stmt : T_OPEN_BRACE nt_dependency_stmt_no_brace T_CLOSE_BRACE
{
  $$ = $2;
}
                   | nt_dependency_stmt_no_brace
{
  $$ = $1;
}


nt_dependency_stmt_no_brace : nt_dependency
{
  $$ = $1;
}
                            | T_IF nt_expr T_THEN nt_dependency 
                              nt_dependency_elseif
{
  if ($5 != NULL)
    $$ = node_alloc(NODETYPE_IF, list_alloc3($2, $4, $5), yyfilename,
                    yylineno);
  else
    $$ = node_alloc(NODETYPE_IF, list_alloc2($2, $4), yyfilename, yylineno);
}

nt_dependency_elseif : T_ELSEIF nt_expr T_THEN nt_dependency 
                       nt_dependency_elseif
{
  $$ = node_alloc(NODETYPE_IF, list_alloc3($2, $4, $5), yyfilename, yylineno);
}
                     | T_ELSE nt_dependency
{
  $$ = $2;
}
                     | /* empty */
{
  $$ = NULL;
}


nt_dependency : T_EQUAL nt_expr_cpd_call
{
  $$ = node_alloc(NODETYPE_DET_DEP, list_alloc1($2), yyfilename, yylineno);
}
              | T_TILDE nt_expr_cpd_call
{
  $$ = node_alloc(NODETYPE_STO_DEP, list_alloc1($2), yyfilename, yylineno);
}

nt_expr_cpd_call : nt_expr_identifier nt_opt_box_expr_list
                   nt_opt_paren_expr_list
{
  $$ = node_alloc(NODETYPE_CPD_CALL, list_alloc3($1, $2, $3), yyfilename,
                  yylineno);
}

nt_opt_box_expr_list : /* empty */
{
  $$ = node_alloc(NODETYPE_LIST, list_alloc(), yyfilename, yylineno);
}
                     | T_OPEN_BOX nt_expr_list T_CLOSE_BOX
{
  $$ = $2;
}

nt_opt_paren_expr_list : /* empty */
{
  $$ = node_alloc(NODETYPE_LIST, list_alloc(), yyfilename, yylineno);
}
                       | T_OPEN_PAREN nt_expr_list T_CLOSE_PAREN
{
  $$ = $2;
}

nt_expr_list : /* empty */
{
  $$ = node_alloc(NODETYPE_LIST, list_alloc(), yyfilename, yylineno);
}
             | nt_expr
{
  $$ = node_alloc(NODETYPE_LIST, list_alloc1($1), yyfilename, yylineno);
}
             | nt_expr T_COMMA nt_expr_list
{
  $$ = $3;
  list_insert_front($$->childlist, $1);
}

nt_expr : T_OPEN_PAREN nt_expr T_CLOSE_PAREN
{
  $$ = $2;
}
        | T_NULL 
{
  nodeleaf leaf;
  leaf.pchar = NULL; /* leaf is unused, init'ing it to keep compiler happy */
  $$ = nodeleaf_alloc(NODETYPE_NULL, leaf, yyfilename, yylineno);
}
        | T_STRING
{
  nodeleaf leaf;
  leaf.pchar = $1;
  $$ = nodeleaf_alloc(NODETYPE_STRING, leaf, yyfilename, yylineno);
}
        | nt_expr_identifier
        | T_NATURAL_NUM
{
  nodeleaf leaf;
  leaf.intval = $1;
  $$ = nodeleaf_alloc(NODETYPE_INTEGER, leaf, yyfilename, yylineno);
}
        | T_REAL_NUM
{
  nodeleaf leaf;
  leaf.doubleval = $1;
  $$ = nodeleaf_alloc(NODETYPE_REAL, leaf, yyfilename, yylineno);
}
        | nt_expr T_AND nt_expr
{
  $$ = node_alloc(NODETYPE_AND, list_alloc2($1, $3), yyfilename, yylineno);
}
        | nt_expr T_OR nt_expr
{
  $$ = node_alloc(NODETYPE_OR, list_alloc2($1, $3), yyfilename, yylineno);
}
        | nt_expr T_EQUAL nt_expr
{
  $$ = node_alloc(NODETYPE_EQUAL, list_alloc2($1, $3), yyfilename, yylineno);
}
        | nt_expr T_NOT_EQUAL nt_expr
{
  $$ = node_alloc(NODETYPE_NOT_EQUAL, list_alloc2($1, $3), yyfilename,
                  yylineno);
}
        | nt_expr_rkvector
{
  $$ = $1;
}
        | nt_expr_fn_call
{
  $$ = $1;
}
        | nt_expr_set_imp
{
  $$ = $1;
}
        | T_SHARP nt_expr_set_imp
{
  $$ = node_alloc(NODETYPE_SET_CARD, list_alloc1($2), yyfilename, yylineno);
}
        | nt_expr_set_exp
{
  $$ = $1;
}


nt_expr_identifier : T_IDENTIFIER
{
  nodeleaf leaf;
  leaf.pchar = $1;
  $$ = nodeleaf_alloc(NODETYPE_IDENTIFIER, leaf, yyfilename, yylineno);
}


nt_expr_rkvector : T_OPEN_BOX nt_expr_list T_CLOSE_BOX
{
  $$ = $2;
  $$->type = NODETYPE_RkVECTOR;
}


nt_expr_fn_call : nt_expr_identifier T_OPEN_PAREN nt_expr_list T_CLOSE_PAREN
{
  $$ = node_alloc(NODETYPE_FN_CALL, list_alloc2($1, $3), yyfilename, yylineno);
}


nt_expr_set_imp : T_OPEN_BRACE nt_typename nt_expr_identifier
                  nt_opt_expr_set_cond T_CLOSE_BRACE 
{
  list_t args;
  args = list_alloc1($3);
  if (NULL != $4)
    list_insert_end(args, $4);
  $3->valtype = $2;
  $$ = node_alloc(NODETYPE_SET_IMP, args, yyfilename, yylineno);
}


nt_opt_expr_set_cond : /* empty */
{
  $$ = NULL;
}
                     | T_COLON nt_expr 
{
  $$ = $2;
}

nt_expr_set_exp : T_OPEN_BRACE nt_id_list T_CLOSE_BRACE
{
  $$ = node_alloc(NODETYPE_SET_EXP, $2, yyfilename, yylineno);
}

nt_number_statement : T_SHARP nt_typename nt_opt_paren_id_equal_id_list
                      nt_dependency_stmt T_SEMICOLON
{
  symbol * sym;
  
  sym = $2;
  
  if (sym->builtin)
  {
    char errmsg[1000];
    /* format the error message */
    sprintf(errmsg, "number statement not allowed on builtin type %s", 
            sym->name);
    /* print the error and stop parsing */
    yyerror(errmsg);
    YYERROR;
  }

  semantic_parse_num($2, $3, $4);
}

nt_opt_paren_id_equal_id_list : /* empty */
{
  $$ = list_alloc();
}
                              | T_OPEN_PAREN nt_id_equal_id_list T_CLOSE_PAREN
{
  $$ = $2;
}

nt_id_equal_id_list : /* empty */
{
  $$ = list_alloc();
}
                    | nt_id_equal_id
{
  $$ = list_alloc1($1);
}
                    | nt_id_equal_id T_COMMA nt_id_equal_id_list
{
  $$ = $3;
  list_insert_front($$, $1);
}

nt_id_equal_id : nt_expr_identifier T_EQUAL nt_expr_identifier
{
  $$ = node_alloc(NODETYPE_EQUAL, list_alloc2($1, $3), yyfilename, yylineno);
}

nt_obs_statement : T_OBS nt_expr T_EQUAL nt_expr T_SEMICOLON
{
  node * obs;

  obs = node_alloc(NODETYPE_EQUAL, list_alloc2($2, $4), yyfilename, yylineno);
  
  semantic_parse_obs(obs);
  
  list_insert_end(prs_obs_list, obs);
}

nt_opt_typename : nt_typename | {$$ = NULL;}

nt_query_statement : T_QUERY nt_expr nt_opt_typename T_SEMICOLON
{
  $2->reqtype = $3;
  
  semantic_parse_qry($2);
  
  list_insert_end(prs_qry_list, $2);
}

%%

#include <stdio.h>

int yyerror (char *s)  /* Called by yyparse on error */
{
  fprintf (stderr, "%s:%d: error: %s at: '%s'\n", yyfilename, yylineno, s, 
           yytext);
  return 0;
}

